package com.apobates.forum.toro.tuple;

import com.apobates.forum.toro.core.BeanParser;
import com.apobates.forum.toro.core.Corner;
import com.apobates.forum.toro.core.Express;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

/**
 * 多个左对象Triple映射语句
 * @param <L> 左对象类型
 * @since 20220827
 * @author xiaofanku@live.cn
 */
public class TripleManyMapperStatements<L> {
    private final static BeanParser<TripleManyMapperStatements.Argument> parser;
    static {
        // M(middleAttr:middleAttrType)#R(rightAttr:rightAttrType)&ML(linkAttr:linkAttrType)#RJ(joinAttr:joinAttrType)
        Express exp = Express.getInstance()
                .word("middleAttr")
                .corner(Corner.sequence(0).edge(":").build())
                .word("middleAttrType")
                .corner(Corner.sequence(1).edge(")#R(").build())
                .word("rightAttr")
                .corner(Corner.sequence(2).edge(":").build())
                .word("rightAttrType")
                .corner(Corner.sequence(3).edge(")&ML(").build())
                .word("linkAttr")
                .corner(Corner.sequence(4).edge(":").build())
                .word("linkAttrType")
                .corner(Corner.sequence(5).edge(")#RJ(").build())
                .word("joinAttr")
                .corner(Corner.sequence(6).edge(":").build())
                .word("joinAttrType")
                .corner(Corner.sequence(7).edge(")").build());
        // 从表达式到实例
        parser = exp.build(TripleManyMapperStatements.Argument.class);
    }
    /**
     * 设置连接M的属性名称。避免与expr并用
     * @param middleAttr 中对象关联的属性名称
     * @return ASF Lang3 Pair连接参数
     */
    public Argument middle(String middleAttr) {
        return new Argument().middle(middleAttr);
    }
    /**
     * 设置参数语句表达式描述
     * 通过描述字符串设置右对象的连接属性。注意：不要与attr混用
     * 格式: M(middleAttr:middleAttrType)#R(rightAttr:rightAttrType)&ML(linkAttr:linkAttrType)#RJ(joinAttr:joinAttrType)
     * @param exprDescribe 查询参数表达式描述
     * @return
     */
    public Argument expr(String exprDescribe) {
        Map<String,Object> argMap = parser.toMap(exprDescribe, "M(");
        return new Argument()
                .middle(argMap.get("middleAttr").toString())
                .middleType(argMap.get("middleAttrType").toString())
                .right(argMap.get("rightAttr").toString())
                .rightType(argMap.get("rightAttrType").toString())
                .link(argMap.get("linkAttr").toString())
                .linkType(argMap.get("linkAttrType").toString())
                .join(argMap.get("joinAttr").toString())
                .joinType(argMap.get("joinAttrType").toString());
    }

    /**
     * ASF Lang3 Pair连接参数
     */
    public class Argument {
        private String middleAttr;
        private String middleAttrType;
        private String rightAttr;
        private String rightAttrType;
        private String joinAttr;
        private String joinAttrType;
        private String linkAttr;
        private String linkAttrType;

        private Argument() {
        }

        /**
         * 设置连接M的属性名称
         *
         * @param middleAttr 连接M的属性名称
         * @return
         */
        public Argument middle(String middleAttr) {
            this.middleAttr = middleAttr;
            return this;
        }

        /**
         * 设置连接M的属性类型
         *
         * @param middleAttrType 关联的属性类型
         * @return
         */
        public Argument middleType(String middleAttrType) {
            this.middleAttrType = middleAttrType;
            return this;
        }

        /**
         * 设置连接R的属性名称
         *
         * @param rightAttr 右对象关联的属性名称
         * @return
         */
        public Argument right(String rightAttr) {
            this.rightAttr = rightAttr;
            return this;
        }

        /**
         * 设置连接R的属性类型
         *
         * @param rightAttrType 右对象关联的属性类型
         * @return
         */
        public Argument rightType(String rightAttrType) {
            this.rightAttrType = rightAttrType;
            return this;
        }

        /**
         * 设置L连接R的属性名称
         *
         * @param joinAttr L连接R的属性名称
         * @return
         */
        public Argument join(String joinAttr) {
            this.joinAttr = joinAttr;
            return this;
        }

        /**
         * 设置L连接R的属性类型
         *
         * @param joinAttrType L连接R的属性类型
         * @return
         */
        public Argument joinType(String joinAttrType) {
            this.joinAttrType = joinAttrType;
            return this;
        }

        /**
         * 设置L连接M的属性名称
         *
         * @param linkAttr L连接M的属性名称
         * @return
         */
        public Argument link(String linkAttr) {
            this.linkAttr = linkAttr;
            return this;
        }

        /**
         * 设置L连接M的属性类型
         *
         * @param linkAttrType L连接M的属性类型
         * @return
         */
        public Argument linkType(String linkAttrType) {
            this.linkAttrType = linkAttrType;
            return this;
        }

        /**
         * 设置左对象集合的查询参数
         * @param leftArgs 左对象的查询参数
         * @return 返回ASF lang3 AnyPair集合聚合执行器
         * @param <T>
         */
        public <L> TripleManyMapperExecutor<L> by(Collection<L> leftArgs) {
            return new TripleManyMapperExecutor<>(()->leftArgs, this.buildArguments());
        }

        /**
         * 设置左对象的供应函数
         * @param leftObjSetFun 左对象集合的供应函数
         * @return
         * @param <L> 左对象的类型
         */
        public <L> TripleManyMapperExecutor<L> supply(Supplier<Collection<L>> leftObjSetFun) {
            return new TripleManyMapperExecutor<>(leftObjSetFun, this.buildArguments());
        }

        private Map<String, String> buildArguments() {
            Map<String, String> arguments = new HashMap<>();
            arguments.put("middleAttr", this.middleAttr);
            arguments.put("middleAttrType", this.middleAttrType);
            arguments.put("rightAttr", this.rightAttr);
            arguments.put("rightAttrType", this.rightAttrType);
            arguments.put("joinAttr", this.joinAttr);
            arguments.put("joinAttrType", this.joinAttrType);
            arguments.put("linkAttr", this.linkAttr);
            arguments.put("linkAttrType", this.linkAttrType);
            return arguments;
        }
    }
}
